#include <stdlib.h>
#include <stdint.h>
#include <string.h>

#include <debug.h>
#include <os_api.h>
#include <device_api.h>
#include <net/http.h>

#define TASK_STACK_SIZE (20 * 1024)
static char _task_stack[TASK_STACK_SIZE];

#define APP_CMD_INIT 1
#define APP_CMD_REPORT 2

typedef struct app_msg_s
{
    uint32_t msg;
    void *data;
} app_msg_t;

typedef struct app_data_s
{
    OSTaskRef task;
    OSTimerRef timer;
    OSMsgQRef cmdq;
} app_data_t;

static int app_send_msg(app_data_t *ad, uint32_t msg_id, void *data)
{
    app_msg_t msg;

    memset(&msg, 0, sizeof(msg));
    msg.msg = msg_id;
    msg.data = data;
    if (OS_SUCCESS == OSAMsgQSend(ad->cmdq, sizeof(msg), (UINT8 *)&msg, OS_NO_SUSPEND))
    {
        return 0;
    }
    return -1;
}

static void app_init_timer_cb(UINT32 param)
{
    app_data_t *ad = (app_data_t *)param;
    app_send_msg(ad, APP_CMD_INIT, NULL);
}

static void app_report_timer_cb(UINT32 param)
{
    app_data_t *ad = (app_data_t *)param;
    app_send_msg(ad, APP_CMD_REPORT, NULL);
}

static int app_http_get_cb(char *buffer, int size, int nitems, void *private_data)
{
    char buf[64];
    size_t cur = 0, sz;

    LOG_PRINTF("%s: buffer %p size %d totalsize %d\n", __func__, buffer, size, nitems);
    while (cur < (size_t)size)
    {
        sz = size - cur;
        if (sz >= sizeof(buf))
            sz = sizeof(buf) - 1;
        memcpy(buf, buffer + cur, sz);
        buf[sz] = '\0';
        LOG_PRINTF("%s: data: %s", __func__, buf);

        cur += sz;
    }
    return 0;
}

static int app_http_get(app_data_t *ad, const char *url)
{
    int ret = 0;
    struct http_client *client = NULL;
    struct http_client_list *header = NULL;
    int http_response_code;

    LOG_PRINTF("%s: +\n", __func__);

    client = http_client_init();
    if (!client)
    {
        ret = -1;
        goto clean;
    }
    http_client_setopt(client, HTTPCLIENT_OPT_URL, url);
    http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB, app_http_get_cb);
    http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB_DATA, ad);
    http_client_setopt(client, HTTPCLIENT_OPT_METHOD, HTTPCLIENT_REQUEST_GET);
    header = http_client_list_append(NULL, "User-Agent: yuge http client\r\n");
    http_client_setopt(client, HTTPCLIENT_OPT_HTTPHEADER, header);
    ret = http_client_perform(client);
    if (ret != HTTP_CLIENT_OK)
    {
        ret = -1;
        goto clean;
    }
    ret = http_client_getinfo(client, HTTPCLIENT_GETINFO_RESPONSE_CODE, &http_response_code);
    if (ret != HTTP_CLIENT_OK)
    {
        ret = -1;
        goto clean;
    }
    if (http_response_code != 200)
    {
        ret = -1;
        goto clean;
    }
    ret = 0;
clean:
    if (client)
        http_client_shutdown(client);
    if (header)
        http_client_list_destroy(header);
    LOG_PRINTF("%s: -\n", __func__);
    return ret;
}

static int app_http_post_cb(char *buffer, int size, int nitems, void *private_data)
{
    char buf[64];
    size_t cur = 0, sz;

    LOG_PRINTF("%s: buffer %p size %d totalsize %d\n", __func__, buffer, size, nitems);
    while (cur < (size_t)size)
    {
        sz = size - cur;
        if (sz >= sizeof(buf))
            sz = sizeof(buf) - 1;
        memcpy(buf, buffer + cur, sz);
        buf[sz] = '\0';
        LOG_PRINTF("%s: data: %s", __func__, buf);

        cur += sz;
    }
    return 0;
}

static int app_http_post(app_data_t *ad, const char *url, const void *data, size_t data_sz)
{
    int ret = 0;
    struct http_client *client = NULL;
    int http_response_code;

    LOG_PRINTF("%s: +\n", __func__);

    client = http_client_init();
    if (!client)
    {
        ret = -1;
        goto clean;
    }
    http_client_setopt(client, HTTPCLIENT_OPT_URL, url);
    http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB, app_http_post_cb);
    http_client_setopt(client, HTTPCLIENT_OPT_RESPONSECB_DATA, ad);
    http_client_setopt(client, HTTPCLIENT_OPT_METHOD, HTTPCLIENT_REQUEST_POST);
    http_client_setopt(client, HTTPCLIENT_OPT_POSTDATA, data);
    http_client_setopt(client, HTTPCLIENT_OPT_POSTLENGTH, data_sz);
    ret = http_client_perform(client);
    if (ret != HTTP_CLIENT_OK)
    {
        ret = -1;
        goto clean;
    }
    ret = http_client_getinfo(client, HTTPCLIENT_GETINFO_RESPONSE_CODE, &http_response_code);
    if (ret != HTTP_CLIENT_OK)
    {
        ret = -1;
        goto clean;
    }
    if (http_response_code != 200)
    {
        ret = -1;
        goto clean;
    }
    ret = 0;
clean:
    if (client)
        http_client_shutdown(client);
    LOG_PRINTF("%s: -\n", __func__);
    return ret;
}

static void app_init(app_data_t *ad)
{
    char buf[256];

    LOG_PRINTF("%s: +", __func__);

    strcpy(buf, "this is a test");
    app_http_post(ad, "http://loc.yuge-info.com/?mcc=460&mnc=00&lac=20&ci=20&csq=26&rssi=26", buf, strlen(buf));

    OSATimerStart(ad->timer, SEC2TICKS(2), SEC2TICKS(60), app_report_timer_cb, (UINT32)ad);
    LOG_PRINTF("%s: -", __func__);
}

static void app_report(app_data_t *ad)
{
    LOG_PRINTF("%s: +", __func__);

    app_http_get(ad, "http://loc.yuge-info.com/?mcc=460&mnc=00&lac=20&ci=20&csq=26&rssi=26");

    LOG_PRINTF("%s: -", __func__);
}

static void app_task(void *param)
{
    app_data_t *ad = (app_data_t *)param;
    int ret;
    app_msg_t msg;

    while (1)
    {
        memset(&msg, 0, sizeof(msg));
        ret = OSAMsgQRecv(ad->cmdq, (UINT8 *)&msg, sizeof(msg), OS_SUSPEND);
        ASSERT(ret == OS_SUCCESS);
        switch (msg.msg)
        {
        case APP_CMD_INIT:
            app_init(ad);
            break;
        case APP_CMD_REPORT:
            app_report(ad);
            break;
        default:
            break;
        }
    }
}

int main()
{
    int ret;
    app_data_t *ad;

    dev_SetAppVersion("HTTP-V1.0");

    ad = malloc(sizeof(*ad));
    ASSERT(ad);
    memset(ad, 0, sizeof(*ad));

    ret = OSATimerCreate(&ad->timer);
    ASSERT(ret == OS_SUCCESS);

    ret = OSAMsgQCreate(&ad->cmdq, "app", sizeof(app_msg_t), 10, OS_FIFO);
    ASSERT(ret == OS_SUCCESS);

    ret = OSATaskCreate(&ad->task, _task_stack, TASK_STACK_SIZE, OS_APP_PRIORITY, "app", app_task, ad);
    ASSERT(ret == OS_SUCCESS);

    ret = OSATimerStart(ad->timer, SEC2TICKS(10), 0, app_init_timer_cb, (UINT32)ad);
    ASSERT(ret == OS_SUCCESS);
}
